{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Defining Custom Node Types"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mathematical expressions are only the first step. Most of the time, in mathematical software, the interesting aspects are special \"things\" that are strung together by expressions.\n",
    "\n",
    "So it would be helpful to be able to define our own expression types:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import pymbolic.primitives as p\n",
    "\n",
    "x = p.Variable(\"x\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class DerivativeOperator(p.Expression):\n",
    "    def __init__(self, operand):\n",
    "        self.operand = operand\n",
    "\n",
    "    def __getinitargs__(self):\n",
    "        return (self.operand,)\n",
    "\n",
    "    mapper_method = \"map_derivative_operator\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`__getinitargs__` tells `pymbolic` what the arguments of the constructor were. This is used for printing and comparisons."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Quotient(Variable('x'), DerivativeOperator(Power(Sum((Variable('x'), 23)), 0.5)))"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "u = x/DerivativeOperator((x + 23)**0.5)\n",
    "u"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can then also define custom mappers (let's call ours `DerivDoubler`) that operate on these node types:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from pymbolic.mapper import IdentityMapper"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#clear\n",
    "class DerivDoubler(IdentityMapper):\n",
    "    def map_derivative_operator(self, expr):\n",
    "        return 2*DerivativeOperator(self.rec(expr.operand))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now apply it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Quotient(Variable('x'), Product((2, DerivativeOperator(Power(Sum((Variable('x'), 23)), 0.5)))))"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dd = DerivDoubler()\n",
    "\n",
    "dd(u)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.0+"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
